Singleton Pattern
✒️ 2025-05-26 09:10 내용 수정
참고 자료 : wikipedia Singleton pattern , 위키백과 싱글턴 패턴, 끝나지 않는 프로그래밍 일기's 디자인 패턴.싱글턴 패턴
클래스의 초기화를 하나의 인스턴스로만 제한을 두는 패턴
- 생성자가 여러 차례 호출되더라도 실제 생성되는 객체는 하나이며, 최초 생성 이후 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다.
- 요청이 잦은 클래스의 경우 하나의 객체만 메모리에 등록해 여러 스레드가 동시에 사용할 수 있어 성능 상 유리하다.
- 메모리에 처음 한 번만 올리기 위해 객체를 static으로 선언하며, 외부에서 생성자에 접근하지 못하도록 하기 위해 생성자는 private으로 제한 설정을 한다.
- 오직 클래스 내의 정적 메소드로만 접근하도록 설정한다.
- 전역 접근성이 좋다는 이유로 과도하게 사용할 경우 테스트에 어려움이 생길 수 있으며, 멀티 스레드 환경에서의 데이터 경쟁이 일어나기 쉬워지고, 싱글턴을 변경할 때 이에 의존하는 다른 클래스들에 문제가 발생하기 쉽다는 문제가 있다.
- 아래 예시 종류는 Java를 기준으로 작성했다.
1. Lazy Initialization Singleton
- getInstance() 메소드로 객체가 초기화되지 않았다면 초기화를 진행하고, 그 이후에는 이미 생성된 객체를 반환한다.
- 멀티 스레드 환경에선 두 스레드가 동시에 접근 시 객체를 두 개 이상 생성할 수도 있어 문제가 발생할 수 있다.
public class LazyInitialSingleton {
private static LazyInitialSingleton single;
public static LazyInitialSingleton getInstance() {
if (single == null) {
single = new LazyInitialSingleton();
}
return single;
}
}
2. Synchronized Singleton
- sychronized를 적용한 Lazy initialization singleton
- 멀티 스레드 환경에서 스레드 안전이 보장된다.
- 싱글톤을 최초 생성할 때만 lock을 거는 것이 아닌, 싱글톤 인스턴스를 불러올 때마다 lock을 걸어 성능이 느리다.
public class SynchSingleton {
private static SynchSingleton single;
public static synchronized SynchSingleton getInstance() {
if (single == null) {
single = new SynchSingleton();
}
return single;
}
}
3. Eager Initialization Singleton
- 1번과 반대로 이른 초기화를 진행한 singleton으로, 클래스 로드 시점에 인스턴스를 생성한다.
- 생성자를 private으로 설정해야 외부에서 접근할 수 없다.
- 이른 초기화로 인해 해당 싱글톤을 사용하지 않아도 인스턴스가 로드되는 문제가 있다.
public class EagerInitialSIngleton {
private static EagerInitialSIngleton single = new EagerInitialSIngleton();
private EagerInitialSIngleton() {
}
public static EagerInitialSIngleton getInstance() {
return single;
}
}
4. Double-Checking-Locking Singleton
- synchronized의 범위를 줄인 Lazy initialization singleton
- 멀티 스레드 환경에서 컴파일러가 최적화라는 명목으로 연산 순서를 변경할 수 있어 직관을 벗어나는 동작이 발생할 수 있으므로, 인스턴스를 volatile로 선언해야 한다.
- volatile가 붙은 인스턴스는 CPU의 캐시를 거치지 않고 바로 메인 메모리에서 read/write가 된다.
public class DCLSingleton {
private static volatile DCLSingleton single;
private DCLSingleton() {
}
public static DCLSingleton getInstance() {
if(single == null) {
synchronized (DCLSingleton.class) {
if(single == null) {
single = new DCLSingleton();
}
}
}
return single;
}
}
5. Enum Singleton
- Eager Initialization Singleton 이다.
- 리플렉션 API를 통해 인스턴스를 만드려는 시도를 쉽게 무력화할 수 있다.
- 쉽게 직렬화를 할 수 있다.
public enum EnumSingleton {
single;
}
6. Lazy Holder Singleton
- Eager Initialization Singleton에서 생성 방법을 개선한 방법이다.
- singleton 클래스엔 LazyHolder 클래스의 변수가 없기 때문에 singleton 클래스를 로딩할 때 LazyHolder 클래스를 초기화하지 않으며, getInstance()를 호출 시에만 LazyHolder 클래스를 초기화한다.
- 클래스를 로딩하고 초기화할 때 thread-safe를 보장한다.
- LazyHolder 내에 선언된 인스턴스가 static이기 때문에 클래스 로딩 시점에 한 번만 호출된다.
public class LazyHolderSingleton {
private LazyHolderSingleton() {
}
public static LazyHolderSingleton getInstance() {
return LazyHolder.single;
}
private static class LazyHolder {
private static final LazyHolderSingleton single = new LazyHolderSingleton();
}
}